
let Chart = function () {
  let _gl
  let _config

  let _pointShaderProgram
  let _pointPositionAttributeLocation
  let _pointSizeAttributeLocation
  let _pointFragColor

  let _lineShaderProgram
  let _linePositionAttributeLocation
  let _lineFragColor

  let points = []
  let pointSizes = []
  let linePoints = []

  let xingzuoCirclePoints1 = []
  let xingzuoCirclePoints2 = []

  // 绘制点的顶点着色器代码
  let pointVertexShaderSource = `
    attribute vec4 a_position;
    attribute float a_PointSize;
    void main() {
        gl_PointSize= a_PointSize;
        gl_Position = a_position;
    }
  `
  // 绘制点的片原着色器代码
  let pointFragmentShaderSource = `
      precision mediump float;
      uniform vec4 u_FragColor;
      void main() {
          float d = distance(gl_PointCoord,vec2(0.5,0.5));
          if(d < 0.5){
            gl_FragColor = u_FragColor;
          }else{
              discard;
          }
      }
  `

  // 绘制线的顶点着色器代码
  let lineVertexShaderSource = `
    attribute vec4 a_position;
    void main() {
        gl_PointSize=100.0;
        gl_Position = a_position;
    }
  `
  // 绘制线的片原着色器代码
  let lineFragmentShaderSource = `
      precision mediump float;
      uniform vec4 u_FragColor;
      void main() {
        gl_FragColor = u_FragColor;
      }
  `

  function _initGl () {
    // 环境上下文准备
    _prepareContext()
    // 编译
    let pointVertexShader = _setupShader(_gl, _gl.VERTEX_SHADER, pointVertexShaderSource)
    let pointFragmentShader = _setupShader(_gl, _gl.FRAGMENT_SHADER, pointFragmentShaderSource)

    // 链接
    _pointShaderProgram = linkShader(_gl, pointVertexShader, pointFragmentShader)
    _pointPositionAttributeLocation = _gl.getAttribLocation(_pointShaderProgram, 'a_position')
    _pointSizeAttributeLocation = _gl.getAttribLocation(_pointShaderProgram, 'a_PointSize')

     // 获取u_FragColor变量的存储位置
    _pointFragColor = _gl.getUniformLocation(_pointShaderProgram, 'u_FragColor')

        // 编译
    let lineVertexShader = _setupShader(_gl, _gl.VERTEX_SHADER, lineVertexShaderSource)
    let lineFragmentShader = _setupShader(_gl, _gl.FRAGMENT_SHADER, lineFragmentShaderSource)

        // 链接
    _lineShaderProgram = linkShader(_gl, lineVertexShader, lineFragmentShader)
    _linePositionAttributeLocation = _gl.getAttribLocation(_lineShaderProgram, 'a_position')

         // 获取u_FragColor变量的存储位置
    _lineFragColor = _gl.getUniformLocation(_lineShaderProgram, 'u_FragColor')

    // 准备画布
    _gl.viewport(0, 0, _gl.canvas.width, _gl.canvas.height)
    _gl.clearColor(_config.r, _config.g, _config.b, 1)
    _gl.clear(_gl.COLOR_BUFFER_BIT)
  }

  function _drawLine (x1, y1, x2, y2) {
// 准备输入数据
    _createBuffer([x1, y1,
      x2, y2 ])
  // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_lineShaderProgram)
    _gl.enableVertexAttribArray(_linePositionAttributeLocation)
  // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
  // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
  // type=gl_FLOAT表示使用的是32为浮点类型的数据。
  // normalize=false表示不需要归一化数据。
  // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)
    _gl.drawArrays(_gl.LINES, 0, 2)
  }

  function _drawCrossLine () {
    // 绘制十字线
    // 准备输入数据
    _createBuffer([-1, 0,
      1, 0,
      0, 1,
      0, -1])
    // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_lineShaderProgram)
    _gl.enableVertexAttribArray(_linePositionAttributeLocation)
    // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
    // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
    // type=gl_FLOAT表示使用的是32为浮点类型的数据。
    // normalize=false表示不需要归一化数据。
    // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)
    _gl.drawArrays(_gl.LINES, 0, 4)
  }

  /**
   * 绘制一个点
   */
  function _drawPoint (x, y, size, r, g, b) {
    // 准备输入数据
    _createBuffer([x, y])
    var sizeData = [size]
     // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_pointShaderProgram)
    _gl.enableVertexAttribArray(_pointPositionAttributeLocation)
    // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
    // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
    // type=gl_FLOAT表示使用的是32为浮点类型的数据。
    // normalize=false表示不需要归一化数据。
    // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_pointPositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_pointFragColor, _config.point_color.r, _config.point_color.g, _config.point_color.b, 1)

    _createBuffer(sizeData)
    _gl.vertexAttribPointer(_pointSizeAttributeLocation, 1, _gl.FLOAT, false, 0, 0)
    _gl.enableVertexAttribArray(_pointSizeAttributeLocation)

    _gl.drawArrays(_gl.POINTS, 0, 1)
  }

  /**
   * 绘制内存中保存的所有点
   */
  function _drawPoints () {
    // 准备输入数据
    _createBuffer(points)
    var sizeData = pointSizes
     // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_pointShaderProgram)
    _gl.enableVertexAttribArray(_pointPositionAttributeLocation)
    // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
    // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
    // type=gl_FLOAT表示使用的是32为浮点类型的数据。
    // normalize=false表示不需要归一化数据。
    // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_pointPositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_pointFragColor, _config.point_color.r, _config.point_color.g, _config.point_color.b, 1)

    _createBuffer(sizeData)
    _gl.vertexAttribPointer(_pointSizeAttributeLocation, 1, _gl.FLOAT, false, 0, 0)
    _gl.enableVertexAttribArray(_pointSizeAttributeLocation)

    _gl.drawArrays(_gl.POINTS, 0, points.length / 2)
  }

  /**
   * 绘制线段组
   * @param {*} points
   */
  function _drawLineGroup (points) {
    _createBuffer(points)
    _gl.useProgram(_lineShaderProgram)
    _gl.enableVertexAttribArray(_linePositionAttributeLocation)
    // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
    // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
    // type=gl_FLOAT表示使用的是32为浮点类型的数据。
    // normalize=false表示不需要归一化数据。
    // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)

    _gl.drawArrays(_gl.LINES, 0, points.length / 2)
  }

  /**
   * 绘制实心圆 (r 0~1)
   */
  function _drawCircle (x, y, banjing, r, g, b, v) {
    var N = 1000
    var vertexData = [x, y]

    for (var i = 0; i <= N; i++) {
      var theta = i * 2 * Math.PI / N
      var x1 = x + banjing * Math.sin(theta)
      var y1 = y + banjing * Math.cos(theta)
      vertexData.push(x1, y1)
    }
    //console.log(vertexData)
    var vertices = new Float32Array(vertexData)
    _createBuffer(vertexData)
     // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_lineShaderProgram)
    _gl.enableVertexAttribArray(_linePositionAttributeLocation)
     // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
     // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
     // type=gl_FLOAT表示使用的是32为浮点类型的数据。
     // normalize=false表示不需要归一化数据。
     // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_lineFragColor, r, g, b, v)

    _gl.drawArrays(_gl.TRIANGLE_FAN, 0, vertices.length / 2)
  }

  /**
   * 绘制圆圈
   * @param {*} vertexData 圆圈上的点
   */
  function _drawHollowCircleWithPoints (vertexData) {
    _createBuffer(vertexData)
    // 加载着色器，准备读取缓冲区的数据
    _gl.useProgram(_lineShaderProgram)
    _gl.enableVertexAttribArray(_linePositionAttributeLocation)
    // gl.vertexAttribPointer(position_attribute_location, size, type, normalize, stride, offset);
    // size=2表示每次迭代读取两个数据，即x和y。由于顶点着色器中gl_Position的类型是vec4，包含x,y,z,w四个数据，而这里只需要前两个x和y。
    // type=gl_FLOAT表示使用的是32为浮点类型的数据。
    // normalize=false表示不需要归一化数据。
    // offset=0表示从缓冲区的开始位置读取数据。
    _gl.vertexAttribPointer(_linePositionAttributeLocation, 2, _gl.FLOAT, false, 0, 0)

    _gl.uniform4f(_lineFragColor, _config.line_color.r, _config.line_color.g, _config.line_color.b, 1)

    _gl.drawArrays(_gl.LINE_LOOP, 0, vertexData.length / 2)
  }

  /**
   * 绘制圆圈
   * @param {*} x 原点x坐标
   * @param {*} y 原点y坐标
   * @param {*} pointSize 线条粗细
   * @param {*} banjing 半径
   * @param {*} r 颜色r值
   * @param {*} g 颜色g值
   * @param {*} b 颜色b值
   * @param {*} v
   */
  function _drawHollowCircle (x, y, pointSize, banjing, r, g, b, v) {
    var N = 2000
    var vertexData = []
    // var sizeData = [pointSize, pointSize]

    for (var i = 0; i <= N; i++) {
      var theta = i * 2 * Math.PI / N
      var x1 = x + banjing * Math.sin(theta)
      var y1 = y + banjing * Math.cos(theta)
      vertexData.push(x1, y1)
    //   sizeData.push(pointSize)
    }
    // //console.log(vertexData)
    _drawHollowCircleWithPoints(vertexData)
    return vertexData
  }

  // 准备WebGL上下文
  function _prepareContext () {
        // 使用Canvas
    let main_canvas = document.querySelector('#' + _config.container)
    // main_canvas.setAttribute('width', '600')
    // main_canvas.setAttribute('height', '600')
    _gl = main_canvas.getContext('webgl')
    if (!_gl) {
      //console.log('无法使用WebGL！')
    } else {
      // //console.log(_gl)
    }
  }
  // 编译
  function _setupShader (gl, type, source) {
    let shader = gl.createShader(type)
    gl.shaderSource(shader, source)
    gl.compileShader(shader)

    let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
    if (success) {
      return shader
    }

    //console.log(gl.getShaderInfoLog(shader))
    gl.deleteShader(shader)
  }
  // 创建缓冲区，并填入顶点数据
  function _createBuffer (data) {
    _gl.bindBuffer(_gl.ARRAY_BUFFER, _gl.createBuffer())
    _gl.bufferData(_gl.ARRAY_BUFFER, new Float32Array(data), _gl.STATIC_DRAW)
  }

  // 链接
  function linkShader (gl, vertexShader, fragmentShader) {
    let program = gl.createProgram()
    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)
    gl.linkProgram(program)
    let success = gl.getProgramParameter(program, gl.LINK_STATUS)
    if (success) {
      return program
    }

    //console.log(gl.getProgramInfoLog(program))
    gl.deleteProgram(program)
  }

  /**
   * 全部涂黑
   */
  function _clear () {
    _gl.clearColor(_config.r, _config.g, _config.b, 1)
    _gl.clear(_gl.COLOR_BUFFER_BIT)
  }
  /**
   * 清空整个区域到原始状态
   */
  function _clearArea () {
    _clear()
    if (_config.type === 'xingzuo') {
      if (xingzuoCirclePoints1.length === 0) {
        xingzuoCirclePoints1 = _drawHollowCircle(0, 0, 1, 0.5, 1, 1, 1)
      } else {
        _drawHollowCircleWithPoints(xingzuoCirclePoints1)
      }
      if (xingzuoCirclePoints2.length === 0) {
        xingzuoCirclePoints2 = _drawHollowCircle(0, 0, 1, 1, 1, 1, 1)
      } else {
        _drawHollowCircleWithPoints(xingzuoCirclePoints2)
      }
      _drawCrossLine()
      _drawLine(1, 1, -1, -1)
      _drawLine(1, -1, -1, 1)
      _drawLine(-1, 0.5, 1, 0.5)
      _drawLine(-1, 0, 1, 0)
      _drawLine(-1, -0.5, 1, -0.5)
      _drawLine(0.5, 1, 0.5, -1)
      _drawLine(-0.5, 1, -0.5, -1)
    }
    if (_config.type === 'eyes') {
      _clear()
    }
  }

  /**
   * 转换坐标成webgl内部坐标
   * @param {*} x
   * @param {*} y
   */
  function _coordinateTransform (x, y) {
    var newX = x
    var newY = y
    if (_config.xAxisInterval) {
      var xpercent = (x - _config.xAxisInterval[0]) / (_config.xAxisInterval[1] - _config.xAxisInterval[0])
      var transformedX = (1 - (-1)) * xpercent + (-1)
      newX = transformedX
    }
    if (_config.xAxisInterval) {
      var percent = (y - _config.yAxisInterval[0]) / (_config.yAxisInterval[1] - _config.yAxisInterval[0])
      var transformedY = (1 - (-1)) * percent + (-1)
      newY = transformedY
    }
    return [newX, newY]
  }

  return {
    /**
     * 星座图
     * @param {} config
     */
    chartXingzuo (config) {
      // //console.log(config)
      _config = config
      _config.type = 'xingzuo'
      if (!_config.bg_color) {
        // 默认背景黑色
        _config.bg_color = {
          r: 0,
          g: 0,
          b: 0
        }
      }
      if (!_config.line_color) {
        _config.line_color = {
          r: 1,
          g: 1,
          b: 1
        }
      }
      if (!_config.point_color) {
        _config.point_color = {
          r: 1,
          g: 1,
          b: 1
        }
      }
      _initGl()
      _clearArea()
    },
    /**
     * 眼图
     * @param {*} config
     */
    chartEyes (config) {
      _config = config
      _config.type = 'eyes'
      if (!_config.bg_color) {
        // 默认背景黑色
        _config.bg_color = {
          r: 0,
          g: 0,
          b: 0
        }
      }
      if (!_config.line_color) {
        _config.line_color = {
          r: 1,
          g: 1,
          b: 1
        }
      }
      if (!_config.point_color) {
        _config.point_color = {
          r: 1,
          g: 1,
          b: 1
        }
      }
      _initGl()
      _clearArea()
    },
    /**
     * 添加一个点并立即绘制
     * @param {*} x
     * @param {*} y
     * @param {*} size
     */
    drawPoint (x, y, size) {
      points.push(x)
      points.push(y)
      pointSizes.push(size)
      _clearArea()
      _drawPoints()
    },
    /**
     * 添加多个点并立即绘制
     * @param {*} newPoints 格式[x1,y1,x2,y2,x3,y3]的数组
     * @param {*} size
     */
    drawPoints (newPoints, size) {
      for (var i = 0; i < newPoints.length; i++) {
        points.push(newPoints[i])
        pointSizes.push(size)
      }
      _clearArea()
      _drawPoints()
    },
    /**
     * 清空数据,刷新图像
     */
    clear () {
      _clearArea()
      points = []
      pointSizes = []
      linePoints = []
    },
    /**
     * 刷新图像
     */
    refresh () {
      _clearArea()
      _drawPoints()
      _drawLineGroup(linePoints)
    },
    /**
     * 添加一条线
     * @param {} newLinelinePoints 线上面的点的坐标数组
     */
    drawLine (newLinelinePoints) {
      var tmp = [...newLinelinePoints]
      for (var i = 0; i < tmp.length; i = i + 2) {
        var transformedXY = _coordinateTransform(newLinelinePoints[i], newLinelinePoints[i + 1])
        tmp[i] = transformedXY[0]
        tmp[i + 1] = transformedXY[1]
      }
      for (var j = 0; j < tmp.length; j++) {
        if (j > 2 && j % 2 === 0) {
          linePoints.push(tmp[j - 2])
          linePoints.push(tmp[j - 1])
        }
        linePoints.push(tmp[j])
      }

      _clearArea()
      _drawLineGroup(linePoints)
    },
    /**
     * 添加多条线
     * @param {*} newLinelinePoints 所有线上面点的坐标数组
     * @param {*} pointNum  每条线含有的点的数量
     */
    drawLines (newLinelinePoints, pointNum) {
      var tmp = [...newLinelinePoints]
      for (var i = 0; i < tmp.length; i = i + 2) {
        var transformedXY = _coordinateTransform(newLinelinePoints[i], newLinelinePoints[i + 1])
        tmp[i] = transformedXY[0]
        tmp[i + 1] = transformedXY[1]
      }
      for (var j = 0; j < tmp.length; j++) {
        if (j % (pointNum * 2) !== 0 && j % (pointNum * 2) > 2 && j % 2 === 0) {
          linePoints.push(tmp[j - 2])
          linePoints.push(tmp[j - 1])
        }
        linePoints.push(tmp[j])
      }
      _clearArea()
      _drawLineGroup(linePoints)
    }
  }
}
export default Chart
